﻿using System;
using System.ServiceModel;
using CalculatorThree.UnitTests.Stubs;
using FluentAssertions;
using FluentAssertions.Assertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using ServiceProxy;

namespace CalculatorThree.UnitTests
{
    [TestClass]
    public class ClientProxyTests
    {
        [TestMethod]
        public void Execute_WhenInnerChannelsMocked_ShouldOperationBeExecutedAndCloseBeCalled()
        {
            //Arrange
            var innerChannel = new Mock<IStubChannel>();
            innerChannel.Setup(i => i.DoSomething()).Returns("Done");
            var clientProxy = new ClientProxy<IStubChannel>(innerChannel.Object);
            
            //Act
            var response = clientProxy.Execute(c => c.DoSomething());

            //Assert
            response.Should().Be("Done");
            innerChannel.Verify(i => i.Close(), Times.Once());
        }

        [TestMethod]
        public void Execute_WhenInnerChannelThrowsCommunicationException_ShouldInnerChannelAbortBeCalled()
        {
            //Arrange
            var innerChannel = new Mock<IStubChannel>();
            innerChannel.Setup(i => i.DoSomething()).Returns("Done");
            innerChannel.Setup(i => i.Close()).Throws(new CommunicationException());
            var clientProxy = new ClientProxy<IStubChannel>(innerChannel.Object);

            //Act
            clientProxy.Execute(c => c.DoSomething());

            //Assert
            innerChannel.Verify(i => i.Abort(), Times.Once());
        }

        [TestMethod]
        public void Execute_WhenInnerChannelThrowsTimeoutException_ShouldInnerChannelAbortBeCalled()
        {
            //Arrange
            var innerChannel = new Mock<IStubChannel>();
            innerChannel.Setup(i => i.DoSomething()).Returns("Done");
            innerChannel.Setup(i => i.Close()).Throws(new TimeoutException());
            var clientProxy = new ClientProxy<IStubChannel>(innerChannel.Object);

            //Act
            clientProxy.Execute(c => c.DoSomething());

            //Assert
            innerChannel.Verify(i => i.Abort(), Times.Once());
        }

        [TestMethod]
        public void Execute_WhenInnerChannelThrowsArgumentException_ShouldArgumentExceptionBeThrown()
        {
            //Arrange
            var innerChannel = new Mock<IStubChannel>();
            innerChannel.Setup(i => i.DoSomething()).Throws(new ArgumentException());
            var clientProxy = new ClientProxy<IStubChannel>(innerChannel.Object);

            //Act
            Action doSomethingAction = () => clientProxy.Execute(c => c.DoSomething());

            //Assert
            doSomethingAction.ShouldThrow<ArgumentException>();
        }
    }
}